using System;
using System.Collections.Generic;
using System.Reflection;
using System.IO;
using System.Diagnostics;

using Tao.OpenGl;

namespace Dida
{
	internal class OpenGLDebugMeshImpl : UnmanagedResource, Dida.Dbg.IDebugMeshImpl, IDisposable
	{
		int list = -1;
		
		static GlTexture tileTexture = null;
		public static void Init()
		{
			tileTexture = new GlTexture(Assembly.GetExecutingAssembly().
				GetManifestResourceStream("Dida.Graphics.tile.png"));
		}

		public OpenGLDebugMeshImpl()
			: base(SdlBinding.UnmanagedResourceCategory)
		{
		}

		protected override void FreeUnmanagedResources()
		{
			Gl.glDeleteLists(1, list);
			list = -1;
		}

		public void Begin()
		{
			Debug.Assert(list == -1, "You shouldn't call begin more than once");

			list = Gl.glGenLists(1);
			Gl.glNewList(list, Gl.GL_COMPILE);
			Gl.glBegin(Gl.GL_TRIANGLES);
			Gl.glColor3f(1, 1, 1);
		}

		public void End()
		{
			Debug.Assert(list != -1,"You should have called Begin before you call End");

			Gl.glEnd();
			Gl.glEndList();
		}

		public void AddTriangle(Vector3D[] verts)
		{
			Vector3D normal = Vector3D.CrossProduct(verts[1] - verts[0], verts[2] - verts[0]);
			normal.Normalize();

			Gl.glNormal3fv(normal.ToFloatArray());
			
			int uAxis, vAxis;
			float uScale,vScale;
			GetCubicProjectionAxis(normal, out uAxis, out vAxis,out uScale,out vScale);

			for (int i = 0; i < 3; i++)
			{
				Gl.glTexCoord2f(verts[i][uAxis] * uScale, verts[i][vAxis] * vScale);
				Gl.glVertex3fv(verts[i].ToFloatArray());
			}
		}

		private static void GetCubicProjectionAxis(Vector3D normal,
			out int uAxis,out int vAxis,out float uScale,out float vScale)
		{
			uScale = vScale = 1;

			float absX = Math.Abs(normal.x);
			float absY = Math.Abs(normal.y);
			float absZ = Math.Abs(normal.z);
			if (absX > absY && absX > absZ)
			{
				uAxis = 2;
				vAxis = 1;

				vScale = -vScale;
				if (normal.x < 0)
					uScale = -uScale;
			}
			else if (absY > absZ)
			{
				uAxis = 0;
				vAxis = 2;

				if (normal.y > 0)
					vScale = -vScale;
			}
			else
			{
				uAxis = 0;
				vAxis = 1;

				vScale = -vScale;
				if (normal.z > 0)
					uScale = -uScale;
			}
		}

		public void Draw(Viewpoint viewpt,Matrix4 mat)
		{
			Debug.Assert(tileTexture != null, "You have to call OpenGLDebugMeshImpl.Init before you can use debug meshes");

			tileTexture.Bind();

			Gl.glEnable(Gl.GL_TEXTURE_2D);
			Gl.glEnable(Gl.GL_COLOR_MATERIAL);
			
			Gl.glCallList(list);

			Gl.glDisable(Gl.GL_TEXTURE_2D);

			Debug.Assert(Gl.glGetError() == Gl.GL_NO_ERROR,"An OpenGl error occured");
		}
	}
}
